﻿#include "luaobj/Luaobj.h"
#include "ZuiLib.h"
#include "xxtea/xxtea.h"
#include "thread/LuaSysThread.h"

using namespace ZuiLib;

extern "C"
{
#ifdef WIN32
	extern int luaopen_socket_core(lua_State *L);
	extern int luaopen_mime_core(lua_State *L);
#endif
	extern int luaopen_lsqlite3(lua_State *L);
	extern int luaopen_cjson(lua_State *l);
	extern int luaopen_utf8(lua_State *L);
	extern int luaopen_lualeveldb(lua_State *L);
};



int luaLoadBuffer(lua_State *L, const char *chunk, int chunkSize, const char *chunkName)
{
	int r = 0;
	const std::string& key = ZScriptEngine::Instance()->GetKey();
	const std::string& sign = ZScriptEngine::Instance()->GetSign();
	if (!sign.empty() && strncmp(chunk, sign.c_str(), sign.length()) == 0)
	{
		// decrypt XXTEA
		xxtea_long len = 0;
		unsigned char* result = xxtea_decrypt((unsigned char*)chunk + sign.length(),
			(xxtea_long)chunkSize - sign.length(),
			(unsigned char*)key.c_str(),
			(xxtea_long)key.length(),
			&len);
		r = luaL_loadbuffer(L, (char*)result, len, chunkName);
		free(result);
	}
	else
	{
		r = luaL_loadbuffer(L, chunk, chunkSize, chunkName);
	}

#ifdef _DEBUG
	if (r)
	{
		switch (r)
		{
		case LUA_ERRSYNTAX:
			LOG(LS_ERROR) << "syntax error during pre-compilation error: " << lua_tostring(L, -1);
			break;

		case LUA_ERRMEM:
			LOG(LS_ERROR) << "memory allocation error " << chunkName;
			break;

		case LUA_ERRFILE:
			LOG(LS_ERROR)<<"cannot open/read file "<<chunkName;
			break;

		default:
			LOG(LS_ERROR) << "load file error " << chunkName;
		}
	}
#endif
	return r;
}


int luaapp_lua_loader(lua_State *L)
{
	static const char* BYTECODE_FILE_EXT = ".luac";
	static const char* NOT_BYTECODE_FILE_EXT = ".lua";

	std::string filename(luaL_checkstring(L, 1));
	size_t pos = filename.rfind(NOT_BYTECODE_FILE_EXT);
	if (pos != std::string::npos)
	{
		filename = filename.substr(0, pos);
	}

	pos = filename.find_first_of(".");
	while (pos != std::string::npos)
	{
		filename.replace(pos, 1, "/");
		pos = filename.find_first_of(".");
	}

	// search file in package.path
	ZRefCountedPtr<ZuiLib::ZFileData> chunk;

	std::string chunkName;
	ZuiLib::ZFileUtils* utils = ZFileUtils::Instance();

	std::string searchpath(ZScriptEngine::Instance()->GetLuaPath());

	size_t begin = 0;
	size_t next = searchpath.find_first_of(";", 0);

	do
	{
		if (next == std::string::npos)
			next = searchpath.length();
		else if (next == 0)
		{
			begin = next + 1;
			next = searchpath.find_first_of(";", begin);
		}

		std::string prefix = searchpath.substr(begin, next);
		if (prefix[0] == '.' && prefix[1] == '/')
		{
			prefix = prefix.substr(2);
		}


		pos = prefix.find("?.lua");

#ifndef DEBUG
		chunkName = prefix.substr(0, pos) + filename + BYTECODE_FILE_EXT;

		chunk = utils->GetFileData(chunkName.c_str());
		if (chunk)
			break;
#endif

		chunkName = prefix.substr(0, pos) + filename + NOT_BYTECODE_FILE_EXT;
		chunk = utils->GetFileData(chunkName.c_str());
		if (chunk)
			break;

		begin = next + 1;
		next = searchpath.find_first_of(";", begin);
	} while (begin < (int)searchpath.length());

	filename.append(NOT_BYTECODE_FILE_EXT);
	if (chunk)
	{
		luaLoadBuffer(L, (char*)chunk->data(), (int)chunk->size(), filename.c_str());
	}
	else
	{
		LOG(LS_ERROR)<<"can not get file data of "<<filename;
		return 0;
	}

	return 1;
}



static luaL_Reg luax_exts[] = {
#ifdef WIN32
	{ "socket", luaopen_socket_core },
	{ "mime", luaopen_mime_core },
#endif
	{ "sqlite3", luaopen_lsqlite3 },
	{ "cjson", luaopen_cjson },
	{ "utf8", luaopen_utf8 },
    {"leveldb",luaopen_lualeveldb },
	{ NULL, NULL }
};






void luaapp_open_extensions(lua_State *L)
{
	// load extensions
	luaL_Reg* lib = luax_exts;
	lua_getglobal(L, "package");
	lua_getfield(L, -1, "preload");
	for (; lib->func; lib++)
	{
		lua_pushcfunction(L, lib->func);
		lua_setfield(L, -2, lib->name);
	}
	lua_pop(L, 2);
}


int luaLog(LuaFuncState& L)
{
	const char* str=L.arg(0).toString();
	if (str)
	{
		LOG(LS_INFO)<<"[LUA] " << str;
	}

	return 0;
}

int luaLogError(LuaFuncState& L)
{
	const char* str = L.arg(0).toString();
	if (str)
	{
		LOG(LS_LUAERROR) << "[LUAERROR] " << str;
	}
	return 0;
}

int luaLogInfo(LuaFuncState& L)
{
	const char* str = L.arg(0).toString();
	if (str)
	{
		LOG(LS_INFO) << "[LUAINFO] " << str;
	}
	return 0;
}


int luaStrFind(LuaFuncState& L)
{
	const char* str = L.arg(0).toString();
	const char* str1 = L.arg(1).toString();

	const char* find = strstr(str, str1);
	if (find)
	{
		return L.lreturn((int)(find-str)+1);
	}
	return 0;
}

int luaGetDpiScaleValue(LuaFuncState& L)
{
    return L.lreturn(ZDpi::Instance()->GetScaleValue());
}


LuaReg extendFuncs[] = {
	{"strFind",luaStrFind },
	{"getDpiScaleValue",luaGetDpiScaleValue },
	{NULL,NULL}
};




void RegisterExtendLibs(LuaState* L)
{
    RegisterLuaThread(L);

	L->addLuaLoader(luaapp_lua_loader);



	luaapp_open_extensions(L->getLuaState());

#ifdef DEBUG
	L->setGlobal("__debug", L->newBool(true));
#endif


	L->setGlobal("utils",L->newLib(extendFuncs));


	L->setGlobal("log", L->newFunction(luaLog));
	L->setGlobal("logi", L->newFunction(luaLogInfo));
	L->setGlobal("loge", L->newFunction(luaLogError));
}
